#include "lib/softwaremixer/pvmresamplerint.h"
#include "lib/softwaremixer/pvmsoftmixerfastint.h"
#include "lib/pvmvoice.h"
#include "pvm/pvmoptions.h"
#include "lib/pvmsystem.h"

#include "lib/memory/pvmmemory.h"

#define DEFAULT_RESAMPLER_BUFFER_SIZE 1024

class PVMVoiceResamplerIntNearest;
class PVMVoiceResamplerIntLinear;

////////////////////////////////////////////////////////////////////////////////
// Nearest                                                                    //
////////////////////////////////////////////////////////////////////////////////

enum TRESAMPLER_FORMAT
{
	EMONO_8,
	ESTEREO_8,
	EMONO_16,
	ESTEREO_16,
	ECUSTOM
};

class PVMVoiceResamplerIntNearest : public IPVMVoiceResamplerInt, public PVMAllocableObject
{
protected:
	enum {bufSizeInBytes = DEFAULT_RESAMPLER_BUFFER_SIZE};

	IPVMVoice* voice;

	char buf[bufSizeInBytes];
	pvm_int32 offsetInBuffer;
	pvm_int32 bytesInBuffer;
	pvm_int32 samplesToFillfromVoice;

	pvm_int32 rate;
	pvm_int32 dstSampleRate;
	pvm_int32 dstBitsPerSample;
	pvm_int32 sampleSize;
	pvm_uint32 nChanels;
	pvm_int32 step;
	pvm_int32 bufferStartPos;
	pvm_int32 srcPos;

	pvm_int32 fillStepSize;

	int resamplerFormat;

	void CalcStepSize();
public:
	PVMVoiceResamplerIntNearest();
	virtual ~PVMVoiceResamplerIntNearest();

	virtual pvm_bool Set(IPVMVoice* voice, pvm_uint32 dstSampleRate, pvm_uint32 bitsPerSample);
	virtual pvm_uint32 FillBuffer(void* data, pvm_uint32 maxSamples);
	virtual pvm_bool IsEmpty();
	virtual void Clear();
	virtual void SetRate(pvm_uint32 rate);
	virtual pvm_uint32 GetRate() {return rate;};
};

PVMVoiceResamplerIntNearest::PVMVoiceResamplerIntNearest()
{
	Clear();
}

PVMVoiceResamplerIntNearest::~PVMVoiceResamplerIntNearest()
{
	Clear();
}

void PVMVoiceResamplerIntNearest::CalcStepSize()
{
	if(!voice||dstSampleRate==0)
		return;

	int srcSampleRate = voice->GetSamplesPerSecond();
	this->step = (int)(((((pvm_int64)srcSampleRate)<<16)/dstSampleRate)*rate/100);
}

pvm_bool PVMVoiceResamplerIntNearest::Set(IPVMVoice* voice, pvm_uint32 dstSampleRate, pvm_uint32 bitsPerSample)
{
	Clear();

	if(voice==NULL)
		return false;

	if(voice->GetChanelsNum()>2)
		return false;

	pvm_uint32 voiceSamplesPerSecod, voiceBitsPerSample;

	this->voice = voice;
	this->voice->GetFormat(voiceSamplesPerSecod, voiceBitsPerSample, nChanels);
	this->dstSampleRate = dstSampleRate;
	this->dstBitsPerSample = bitsPerSample;
	this->sampleSize = dstBitsPerSample>>3;
	this->srcPos = 0;
	this->fillStepSize = nChanels*sampleSize;
	samplesToFillfromVoice = (bufSizeInBytes/fillStepSize)*nChanels;

	resamplerFormat = ECUSTOM;
	if(nChanels==1)
	{
		if(dstBitsPerSample==8)
		{
			resamplerFormat = EMONO_8;
		}
		else if(dstBitsPerSample==16)
		{
			resamplerFormat = EMONO_16;
		}
	}
	else if(nChanels==2)
	{
		if(dstBitsPerSample==8)
		{
			resamplerFormat = ESTEREO_8;
		}
		else if(dstBitsPerSample==16)
		{
			resamplerFormat = ESTEREO_16;
		}
	}

	CalcStepSize();

	return true;
}

void PVMVoiceResamplerIntNearest::Clear()
{
	voice = 0;

	offsetInBuffer = 0;
	bytesInBuffer = 0;
	samplesToFillfromVoice = 0;

	rate = 100;
	dstSampleRate = 0;
	dstBitsPerSample = 0;
	sampleSize = 0;
	nChanels = 0;
	step = 0;
	bufferStartPos = 0;
	srcPos = 0;
	
	fillStepSize = 0;
}

void PVMVoiceResamplerIntNearest::SetRate(pvm_uint32 rate)
{
	//if(rate<=0)
	//	return;

	this->rate = rate;
	CalcStepSize();
}
/*#if defined(WIN32) && defined(_DEBUG)

#define PERF_DEFS\
	LARGE_INTEGER performanceCount1;\
	LARGE_INTEGER performanceCount2;\

#define PERF_ENTER_FUNC\
	QueryPerformanceCounter(&performanceCount1);

#define PERF_EXIT_FUNC\
	{\
		QueryPerformanceCounter(&performanceCount2);\
		stSamplesTotal += samplesProcessed;\
		stWorkTime += performanceCount2.QuadPart - performanceCount1.QuadPart;\
		stCallCount++;\
	}
#else
#define PERF_DEFS
#define PERF_ENTER_FUNC
#define PERF_EXIT_FUNC
#endif*/

pvm_uint32 PVMVoiceResamplerIntNearest::FillBuffer(void* data, pvm_uint32 maxSamples)
{
	pvm_int32 samplesProcessed;

	switch(resamplerFormat)
	{
	case EMONO_16:
		{
			pvm_int16* dstPtr = (short*)data;
			pvm_int16* dstptrMax = dstPtr + maxSamples;
			pvm_int32 offsetInBuffer;
			pvm_int32 filledByVoice;

			while(dstPtr<dstptrMax)
			{
				offsetInBuffer = (srcPos>>15)&0xFFFFFFFE;

				if(offsetInBuffer>=bytesInBuffer)
				{
					//filling internal buffer
					filledByVoice = FillBufferFromVoice(voice, buf, samplesToFillfromVoice, dstBitsPerSample);
					if(filledByVoice<=0)
					{
						//no more data
						bytesInBuffer = 0;
						samplesProcessed = maxSamples - (dstptrMax - dstPtr);
						return samplesProcessed;
					}

					bytesInBuffer = filledByVoice*2;
					srcPos = 0;
					continue;
				}

				*dstPtr = *((short*)(((char*)buf) + offsetInBuffer));
				//pointers and offset calculations
				srcPos += step;
				dstPtr++;
			}
			samplesProcessed = maxSamples;
		}
		break;		
	/*case ESTEREO_16:
		{
			pvm_int32 dstOffset = 0;
			pvm_int32 samplesToFill = maxSamples;
			while(samplesToFill>0)
			{
				pvm_int32 offsetInBuffer = ((srcPos - bufferStartPos)>>16)*fillStepSize;

				if((bytesInBuffer - offsetInBuffer)<=0)
				{
					//filling internal buffer
					int filledByVoice = FillBufferFromVoice(voice, buf, samplesToFillfromVoice, dstBitsPerSample);
					if(filledByVoice<=0)
					{
						//no more data
						bytesInBuffer = 0;
						break;
					}
					bytesInBuffer = filledByVoice*sampleSize;
					bufferStartPos = srcPos;

					offsetInBuffer = ((srcPos - bufferStartPos)>>16)*fillStepSize;
				}

				if(bytesInBuffer<=offsetInBuffer)
				{
					bytesInBuffer = 0;
					break;
				}

				switch(fillStepSize)
				{
				case 1:
					*(((char*)data) + dstOffset) = *(((char*)buf) + offsetInBuffer);
					// *((char*)dstPtr) = *(((char*)buf) + offsetInBuffer);
					break;
				case 2:
					*((short*)(((char*)data) + dstOffset)) = *((short*)(((char*)buf) + offsetInBuffer));
					// *((short*)dstPtr) = *((short*)(((char*)buf) + offsetInBuffer));
					break;
				case 4:
					*((long*)(((char*)data) + dstOffset)) = *((long*)(((char*)buf) + offsetInBuffer));
					// *((long*)dstPtr) = *((long*)(((char*)buf) + offsetInBuffer));
					break;
				default:
					memcpy(((char*)data) + dstOffset, ((char*)buf) + offsetInBuffer, fillStepSize);
					//memcpy(dstPtr, ((char*)buf) + offsetInBuffer, fillStepSize);
					break;
				}

				//pointers calculations
				srcPos += step;

				samplesToFill -= nChanels;
				dstOffset += fillStepSize;
				//dstPtr = ((char*)dstPtr) + fillStepSize;
			}

			samplesProcessed = maxSamples - samplesToFill;
		}
		break;*/
	default:
		{
			pvm_int32 dstOffset = 0;
			pvm_int32 samplesToFill = maxSamples;
			while(samplesToFill>0)
			{
				pvm_int32 offsetInBuffer = ((srcPos - bufferStartPos)>>16)*fillStepSize;

				if((bytesInBuffer - offsetInBuffer)<=0)
				{
					//filling internal buffer
					int filledByVoice = FillBufferFromVoice(voice, buf, samplesToFillfromVoice, dstBitsPerSample);
					if(filledByVoice<=0)
					{
						//no more data
						bytesInBuffer = 0;
						break;
					}
					bytesInBuffer = filledByVoice*sampleSize;
					bufferStartPos = srcPos;

					offsetInBuffer = ((srcPos - bufferStartPos)>>16)*fillStepSize;
				}

				if(bytesInBuffer<=offsetInBuffer)
				{
					bytesInBuffer = 0;
					break;
				}

				switch(fillStepSize)
				{
				case 1:
					*(((char*)data) + dstOffset) = *(((char*)buf) + offsetInBuffer);
					//*((char*)dstPtr) = *(((char*)buf) + offsetInBuffer);
					break;
				case 2:
					*((short*)(((char*)data) + dstOffset)) = *((short*)(((char*)buf) + offsetInBuffer));
					//*((short*)dstPtr) = *((short*)(((char*)buf) + offsetInBuffer));
					break;
				case 4:
					*((long*)(((char*)data) + dstOffset)) = *((long*)(((char*)buf) + offsetInBuffer));
					//*((long*)dstPtr) = *((long*)(((char*)buf) + offsetInBuffer));
					break;
				default:
					memcpy(((char*)data) + dstOffset, ((char*)buf) + offsetInBuffer, fillStepSize);
					//memcpy(dstPtr, ((char*)buf) + offsetInBuffer, fillStepSize);
					break;
				}

				//pointers calculations
				srcPos += step;

				samplesToFill -= nChanels;
				dstOffset += fillStepSize;
				//dstPtr = ((char*)dstPtr) + fillStepSize;
			}

			samplesProcessed = maxSamples - samplesToFill;
		}
		break;
	}

	return samplesProcessed;
}

pvm_bool PVMVoiceResamplerIntNearest::IsEmpty()
{
	return bytesInBuffer==0;
}

////////////////////////////////////////////////////////////////////////////////
// Linear                                                                     //
////////////////////////////////////////////////////////////////////////////////

class PVMVoiceResamplerIntLinear : public IPVMVoiceResamplerInt, public PVMAllocableObject
{
protected:
	enum {bufSizeInBytes = DEFAULT_RESAMPLER_BUFFER_SIZE};

	IPVMVoice* voice;

	char buf[bufSizeInBytes];
	pvm_int32 bytesInBuffer;
	pvm_int32 samplesToFillfromVoice;

	pvm_int32 rate;
	pvm_int32 dstSampleRate;
	pvm_int32 dstBitsPerSample;
	pvm_int32 sampleSize;
	pvm_uint32 nChanels;
	pvm_int32 step;
	pvm_int32 bufferStartPos;
	pvm_int32 srcPos;

	pvm_int32 fillStepSize;
	void CalcStepSize();
public:
	PVMVoiceResamplerIntLinear();
	virtual ~PVMVoiceResamplerIntLinear();

	virtual pvm_bool Set(IPVMVoice* voice, pvm_uint32 dstSampleRate, pvm_uint32 bitsPerSample);
	virtual pvm_uint32 FillBuffer(void* data, pvm_uint32 maxSamples);
	virtual pvm_bool IsEmpty();
	virtual void Clear();
	virtual void SetRate(pvm_uint32 rate);
	virtual pvm_uint32 GetRate() {return rate;};
protected:
};

PVMVoiceResamplerIntLinear::PVMVoiceResamplerIntLinear()
{
	Clear();
}

PVMVoiceResamplerIntLinear::~PVMVoiceResamplerIntLinear()
{
	Clear();
}

void PVMVoiceResamplerIntLinear::CalcStepSize()
{
	if(!voice||dstSampleRate==0)
		return;

	int srcSampleRate = voice->GetSamplesPerSecond();

	this->step = (int)(((((pvm_int64)srcSampleRate)<<16)/dstSampleRate)*rate/100);
}

pvm_bool PVMVoiceResamplerIntLinear::Set(IPVMVoice* voice, pvm_uint32 dstSampleRate, pvm_uint32 bitsPerSample)
{
	Clear();

	if(voice==NULL)
		return false;

	if(voice->GetChanelsNum()>2)
		return false;

	pvm_uint32 voiceSamplesPerSecod, voiceBitsPerSample;

	this->voice = voice;
	this->voice->GetFormat(voiceSamplesPerSecod, voiceBitsPerSample, nChanels);
	this->dstSampleRate = dstSampleRate;
	this->dstBitsPerSample = bitsPerSample;
	this->sampleSize = dstBitsPerSample>>3;
	this->srcPos = 0;
	this->fillStepSize = nChanels*sampleSize;
	samplesToFillfromVoice = (bufSizeInBytes/fillStepSize)*nChanels;
	CalcStepSize();
	return true;
}

void PVMVoiceResamplerIntLinear::Clear()
{
	voice = 0;

	bytesInBuffer = 0;
	samplesToFillfromVoice = 0;
	bufferStartPos = 0;

	rate = 100;
	dstSampleRate = 0;
	dstBitsPerSample = 0;
	sampleSize = 0;
	nChanels = 0;
	step = 0;
	srcPos = 0;

	fillStepSize = 0;
}

void PVMVoiceResamplerIntLinear::SetRate(pvm_uint32 rate)
{
	//if(rate<=0)
	//	return;
	this->rate = rate;
	CalcStepSize();
}

pvm_uint32 PVMVoiceResamplerIntLinear::FillBuffer(void* data, pvm_uint32 maxSamples)
{
	pvm_int32 samplesToFill = maxSamples;
	pvm_int32 dstOffset = 0;
	pvm_bool voiceEmpty = false;

	while(samplesToFill>0)
	{
		pvm_int32 offsetInBuffer = ((srcPos - bufferStartPos)>>16)*fillStepSize;

		if((bytesInBuffer - offsetInBuffer)<=0)
		{
			//filling internal buffer
			int filledByVoice = FillBufferFromVoice(voice, buf, samplesToFillfromVoice, dstBitsPerSample);
			if(filledByVoice<=0)
			{
				//no more data
				bytesInBuffer = 0;
				break;
			}
			bytesInBuffer = filledByVoice*sampleSize;
			bufferStartPos = srcPos;

			offsetInBuffer = ((srcPos - bufferStartPos)>>16)*fillStepSize;
		}
		else if((bytesInBuffer - offsetInBuffer)==fillStepSize)
		{
			if(voiceEmpty)
			{
				bytesInBuffer = 0;
				break;
			}

			//filling internal buffer
			memcpy(buf, (((char*)buf) + offsetInBuffer), fillStepSize);
			int filledByVoice = FillBufferFromVoice(voice, ((char*)buf) + fillStepSize, samplesToFillfromVoice - nChanels, dstBitsPerSample);
			if(filledByVoice<=0)
			{
				filledByVoice = 0;
				voiceEmpty = true;
			}
			bytesInBuffer = fillStepSize + filledByVoice*sampleSize;
			bufferStartPos = srcPos;

			offsetInBuffer = ((srcPos - bufferStartPos)>>16)*fillStepSize;
		}

		if(bytesInBuffer<=offsetInBuffer)
		{
			bytesInBuffer = 0;
			break;
		}

		unsigned int curPos = srcPos - bufferStartPos;
		
		if((bytesInBuffer - offsetInBuffer)==fillStepSize)
		{
			//no more data in voice
			switch(fillStepSize)
			{
			case 1:
				*(((char*)data) + dstOffset) = *(((char*)buf) + offsetInBuffer);
				break;
			case 2:
				*((short*)(((char*)data) + dstOffset)) = *((short*)(((char*)buf) + offsetInBuffer));
				break;
			case 4:
				*((long*)(((char*)data) + dstOffset)) = *((long*)(((char*)buf) + offsetInBuffer));
				break;
			default:
				memcpy(((char*)data) + dstOffset, ((char*)buf) + offsetInBuffer, fillStepSize);
				break;
			}
		}
		else
		{
			//copying sample to output buffer
			switch(dstBitsPerSample)
			{
			case 8:
				{
					char *srcDataPtrL = (((char*)buf) + offsetInBuffer);
					char *srcDataPtrR = (((char*)buf) + offsetInBuffer + fillStepSize);
					char *dstDataPtr = (((char*)data) + dstOffset);
					for(unsigned int i=0; i<nChanels; i++)
					{
						*dstDataPtr = ((((int)(curPos&0xffff))*(*srcDataPtrR - *srcDataPtrL))>>16) + *srcDataPtrL;
						dstDataPtr++;
						srcDataPtrL++;
						srcDataPtrR++;
					}
				};
				break;
			case 16:
				{
					short *srcDataPtrL = (short*)(((char*)buf) + offsetInBuffer);
					short *srcDataPtrR = (short*)(((char*)buf) + offsetInBuffer + fillStepSize);
					short *dstDataPtr = (short*)(((char*)data) + dstOffset);
					for(unsigned int i=0; i<nChanels; i++)
					{
						*dstDataPtr = ((((int)(curPos&0xffff))*(*srcDataPtrR - *srcDataPtrL))>>16) + *srcDataPtrL;
						dstDataPtr++;
						srcDataPtrL++;
						srcDataPtrR++;
					}
				};
				break;
			};
		}

		//pointers calculations
		srcPos += step;

		samplesToFill -= nChanels;
		dstOffset += fillStepSize;
	}

	return maxSamples - samplesToFill;
}

pvm_bool PVMVoiceResamplerIntLinear::IsEmpty()
{
	return bytesInBuffer==0;
}


//********************************************************************************
//Factory
//********************************************************************************

IPVMVoiceResamplerInt* PVMCreateVoiceResamplerInt(pvm_uint32 type)
{
	switch(type)
	{
	case EResamplerNearest:
		return new PVMVoiceResamplerIntNearest();
	case EResamplerLinear:
		return new PVMVoiceResamplerIntLinear();
///	case EResampleSpline:
//		return new PVMVoiceResamplerIntSpline();
//	case EResampleFir:
//		return new PVMVoiceResamplerIntFir();
	};

	return 0;
}

#undef DEFAULT_RESAMPLER_BUFFER_SIZE
